__asm__(".code16gcc");

#include "Loader.h"


// 临时GDT表
// 这里的FFFF和CF表示的都是界限
// 主要的问题是0x9A和0x92
// https://blog.csdn.net/sinat_56559405/article/details/147014890
// C是1100 G位1，表示4kb，D位1，表示默认32位操作数
// 9A00 - 1001 1010， 可读的代码段
// 9200 - 1001 0010， 可读可写的数据段
// 1001 P表示段存在，S表示数据段，DPL表示权限等级，PL表示权限等级，A表示段存在
uint16_t gdtTable[][4] = {
    {0, 0, 0, 0},
    {0xFFFF, 0x0000, 0x9A00, 0x00CF},
    {0xFFFF, 0x0000, 0x9200, 0x00CF},
};

MemoryInfoType memoryInfo;

// Loader加载器的初始化
void InitLoader(void)
{
    GetMemoryInfo();
    EnterProtectMode();
}


void GetMemoryInfo(void)
{
	// 下面这一段是固定的0x15 BIOS中断获取内存信息的方式
	uint32_t contID = 0;
	SystemManagementAddressMapType addressMap;
	int signature, bytes;

	// 初次：EDX=0x534D4150,EAX=0xE820,ECX=24,INT 0x15, EBX=0（初次）
	// 后续：EAX=0xE820,ECX=24,
	// 结束判断：EBX=0
	memoryInfo.memoryRegionCount = 0;
	for (int i = 0; i < MEMORY_INFO_SIZE; i++) {
		SystemManagementAddressMapType * map = &addressMap;

		__asm__ __volatile__("int  $0x15"
			: "=a"(signature), "=c"(bytes), "=b"(contID)
			: "a"(0xE820), "b"(contID), "c"(24), "d"(0x534D4150), "D"(map));
		if (signature != 0x534D4150) {
			return;
		}

		// todo: 20字节
		if (bytes > 20 && (map->ACPI & 0x0001) == 0){
			continue;
		}

        // 保存RAM信息，只取32位，空间有限无需考虑更大容量的情况
        if (map->Type == 1) {
            memoryInfo.memoryRegionArray[memoryInfo.memoryRegionCount].start = map->BaseL;
            memoryInfo.memoryRegionArray[memoryInfo.memoryRegionCount].size = map->LengthL;
            memoryInfo.memoryRegionCount++;
        }

		if (contID == 0) {
			break;
		}
	}
}

/// @brief 进入保护模式
/// @param  
void EnterProtectMode(void)
{
    // 进入保护模式需要几个步骤？

    // 关中断
    Cli();

    // 开启A20地址线，怎么开？0x92端口寄存器的第二位写入1
    uint8_t temp = Inb(0x92);
    Outb(0x92, temp | 0x02);

    // 加载GDT
	Lgdt((uint32_t)gdtTable, sizeof(gdtTable));

    // 打开CR0
	uint32_t temp1 = ReadCr0();
	WriteCr0(temp1 | 0x01);

    // 使用长跳转，以便清空流水线，将里面的16位代码给清空
	FarJump(0x08, (uint32_t)ProtectModeEntry);
}